package sg.com.techstudio.tafep.dao;

import java.util.Arrays;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import sg.com.techstudio.tafep.filter.RegisterFilter;
import sg.com.techstudio.tafep.obj.CompanyObj;
import sg.com.techstudio.tafep.obj.ParticipantObj;
import sg.com.techstudio.tafep.obj.RegisterObj;

@Repository("registerDao")
@Transactional
public class RegisterDao {
	private static final Log log = LogFactory.getLog(RegisterDao.class);
	
	@Autowired
	private SessionFactory sessionFactory;
	
	@Autowired
	private EmailDao emailDao;
	
	@SuppressWarnings("unchecked")
	@Transactional(readOnly=true)
	public List<RegisterObj> getAllRegisterObj(){
		log.debug("update() entry");
		Criteria c = sessionFactory.getCurrentSession().createCriteria(RegisterObj.class);
		c.addOrder(Order.asc("id"));
		c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		return c.list();
	}
	
	@Transactional(readOnly=true)
	public RegisterObj get(Long id) {
		return (RegisterObj) sessionFactory.getCurrentSession().get(RegisterObj.class, id);
	}
	
	public void update(RegisterObj r) {
		log.debug("update() entry");
		r.setUpdatedDate(Calendar.getInstance().getTime());
		sessionFactory.getCurrentSession().update(r);
	}
	
	public void saveRegistration(RegisterObj r) {
		log.debug("saveRegistration() entry");
		r.setCreatedDate(Calendar.getInstance().getTime());
		sessionFactory.getCurrentSession().save(r);
		log.debug("saveRegistration() exit");
	}
	
	public void saveOrUpdate(RegisterObj r) {
		log.debug("saveOrUpdate() entry");
		r.setCreatedDate(Calendar.getInstance().getTime());
		sessionFactory.getCurrentSession().saveOrUpdate(r);
		log.debug("saveOrUpdate() exit");
	}
	
	public void initParticipantList(RegisterObj r) {
		log.debug("initParticipantList() entry");
		if(r.getParticipantList()==null)
			r.setParticipantList(new LinkedList<ParticipantObj>());
		if(r.getParticipantList().size()>r.getNoParticipant())
			while(r.getParticipantList().size()>r.getNoParticipant()
					&& r.getParticipantList().size()>0)
				r.getParticipantList().remove(r.getParticipantList().size()-1);
		else if(r.getParticipantList().size()<r.getNoParticipant())
			while(r.getParticipantList().size()<r.getNoParticipant())
				r.getParticipantList().add(new ParticipantObj(r));
	}
	
	public void translateProgrammePackage(RegisterObj r) {
		if(r==null||r.getParticipantSize()==0)
			return;
		for(ParticipantObj p: r.getParticipantList())
			translateProgrammePackage(p);
	}
	
	public int countParticipantInList(List<RegisterObj> registerList) {
		log.debug("countParticipantInList() entry");
		if(registerList==null)
			return 0;
		int count = 0;
		for(RegisterObj r: registerList) {
			if(r.getParticipantList()==null)
				continue;
			count+=r.getParticipantSize();
		}
		return count;
	}
	
	protected void translateProgrammePackage(ParticipantObj p) {
		if(p.getPackageId()==null) {
			p.setMain(false);
			p.setBreakout1(null);
			p.setBreakout2(null);
			return;
		}
		p.setMain(p.getPackageId()==1 || p.getPackageId()==2);
		if(p.getPackageId()==2) {
			p.setBreakout1(null);
			p.setBreakout2(null);
		} else {
			p.setBreakout1(p.getPackageId()==1?p.getBreakoutChoice()[0]:p.getBreakoutChoice()[2]);
			p.setBreakout2(p.getPackageId()==1?p.getBreakoutChoice()[1]:p.getBreakoutChoice()[3]);
		}
	}
	
	public void copyCompanyInfo(RegisterObj r, List<CompanyObj> companyList) {
		log.debug("copyCompanyInfo() entry");
		if(companyList==null)
			return;
		for(CompanyObj c: companyList) {
			if(r.getCompanyId().equals(c.getId())) {
				r.setCompanyName(c.getName());
				r.setCompanyAddress(c.getAddress());
				return;
			}
		}
		log.debug("copyCompanyInfo() exit. company not found.");
	}
	
	public void clearCompanyInfo(RegisterObj r) {
		if(r==null)
			return;
		r.setCompanyId(null);
		r.setCompanyName(null);
		r.setCompanyAddress(null);
	}

	
	public void clearParticipantProgramme(ParticipantObj p) {
		if(p==null)
			return;
		p.setMain(false);
		p.setBreakout1(null);
		p.setBreakout2(null);
		p.setPost1(false);
		p.setPost2(false);
	}
	
	public void deleteLastParticipant(RegisterObj r) {
		log.debug("deleteLastParticipant() entry");
		if(r==null||r.getParticipantList()==null
				|| r.getParticipantList().isEmpty())
			return;
		r.getParticipantList().remove(r.getParticipantList().size()-1);
	}
	public void deleteAllParticipant(RegisterObj r) {
		log.debug("deleteAllParticipant() entry");
		if(r==null||r.getParticipantList()==null
				|| r.getParticipantList().isEmpty())
			return;
		r.getParticipantList().clear();
	}
	
	public void addParticipant(RegisterObj r, ParticipantObj p) {
		log.debug("addParticipant() entry");
		if(r==null||p==null)
			return;
		p.setCompany(r.getCompanyName());//copy company name for registrant counting
		r.getParticipantList().add(p);
	}
	
	public Long getNoRegisteredByCompany(RegisterObj r) {
		log.debug("getNoRegisteredByCompany() entry");
		Criteria c = sessionFactory.getCurrentSession().createCriteria(ParticipantObj.class);
		c.add(Restrictions.eq("company", r.getCompanyName()));
		c.setProjection(Projections.rowCount());
		return (Long) c.uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	protected List<ParticipantObj> getParticipantListByCompany(RegisterObj r){
		log.debug("getParticipantListByCompany() entry");
		Criteria c = sessionFactory.getCurrentSession().createCriteria(ParticipantObj.class);
		c.add(Restrictions.eq("company", r.getCompanyName()));
		return c.list();
	}
	
	public void computeAmount(RegisterObj r, List<Boolean> fullFlag) {
		log.debug("computeAmount() entry");
		if(r==null)
			return;
		double total = 0;
		boolean isPledger = r.getPledger();
//		long registered = getNoRegisteredByCompany(r);
//		long i=registered;
		
		List<ParticipantObj> registeredParticipantList=null;
		int[] registeredPerPackage = new int[5];
		long noRegisteredFirstThree=0;
		if(isPledger) {
			registeredParticipantList = getParticipantListByCompany(r);
			if(registeredParticipantList!=null)
			for(ParticipantObj p: registeredParticipantList) {
				if(p.getPackageId()!=null && p.getPackageId()>0
						&& p.getPackageId()<=registeredPerPackage.length) {
					registeredPerPackage[p.getPackageId()-1]++;
					noRegisteredFirstThree++;
				}
				if(p.isPost1())
					registeredPerPackage[3]++;
				if(p.isPost2())
					registeredPerPackage[4]++;
			}
		}
		
//		long noSnefFreeGiven = 0;
//		if(r.isSnefMember()) {
//			registeredParticipantList = getParticipantListByCompany(r);
//			for(ParticipantObj p: registeredParticipantList) {
//				Integer packageId=p.getPackageId();
//				boolean registerFirst3 = packageId!=null && packageId>=1 && packageId<=3;
//				if(registerFirst3 && (p.isPost1() || p.isPost2()))
//					noSnefFreeGiven++;
//			}
//		}
		
		if(r.getParticipantList()!=null)
		for(ParticipantObj p: r.getParticipantList()) {
			double amount = 0;
			Integer packageId=p.getPackageId();

			//additional validation for full capacity check
			if(packageId!=null) {
				if(fullFlag.get(0) && (packageId==1 || packageId==2))
					packageId=null;
				if(fullFlag.get(1)&&fullFlag.get(2)&&fullFlag.get(3)&&fullFlag.get(4)
						&& (packageId==1 || packageId==3))
					packageId=null;
			}
			if(fullFlag.get(5) && p.isPost1())
				p.setPost1(false);
			if(fullFlag.get(6) && p.isPost2())
				p.setPost2(false);
			
			p.setPackageId(packageId); //sync back to object
			
			boolean registerFirst3 = packageId!=null && packageId>=1 && packageId<=3;
			Arrays.fill(p.getSubamount(), 0.0);
			
			
			/*
			if(registerFirst3) {
				if(isPledger && registeredPerPackage[packageId]<=1)
					registeredPerPackage[packageId]++;
				else {
					switch(packageId) {
					case 1:
						amount+=100; p.getSubamount()[0]=100.0;
						break;
					case 2:
						amount+=80; p.getSubamount()[1]=80.0;
						break;
					case 3:
						amount+=50; p.getSubamount()[2]=50.0;
						break;
					}
				}
					
			}
			if(p.isPost1()) {
				if(isPledger && registeredPerPackage[3]<=1)
					registeredPerPackage[3]++;
				else if(r.isSnefMember() && registerFirst3) {
					//snef get free
				} else {
					amount+=50; p.getSubamount()[3]=50.0;
				}
			}
			if(p.isPost2()) {
				if(isPledger && registeredPerPackage[4]<=1)
					registeredPerPackage[4]++;
				else if(r.isSnefMember() && registerFirst3) {
					//snef get free
				} else {
					amount+=30; p.getSubamount()[4]=30.0;
				}
			}
			*/
//			if(!(isPledger && firstNFree(i))) { //first n pledger is free
//				boolean snefGiveFreePost = false;
				if(registerFirst3) {
					if(isPledger && noRegisteredFirstThree<2)
						noRegisteredFirstThree++;
					else
						switch(packageId) {
						case 1:
							amount+=100; p.getSubamount()[0]=100.0;
							break;
						case 2:
							amount+=80; p.getSubamount()[1]=80.0;
							break;
						case 3:
							amount+=50; p.getSubamount()[2]=50.0;
							break;
						}
				}
				//force register post for snef
				if(r.isSnefMember() && registerFirst3) {
					p.setPost1(!fullFlag.get(5)); //register if there's still space
					p.setPost2(!fullFlag.get(6));
				}
				if(p.isPost1()) {
					if(r.isSnefMember() && registerFirst3) { //free case: unlimited for snef
					} else if(isPledger && registeredPerPackage[3]<2) { //free case: first 2 for pledger
						registeredPerPackage[3]++;
					} else {
						amount+=50; p.getSubamount()[3]=50.0;
					}
				}
				if(p.isPost2()) {
					if(r.isSnefMember() && registerFirst3) {
					} else if(isPledger && registeredPerPackage[4]<2) { //free case: first 2 for pledger
						registeredPerPackage[4]++;
					} else {
						amount+=30; p.getSubamount()[4]=30.0;
					}
				}
//				if(isFullConference(p)) { //full conf
//					amount+=getFullConferencePrice(isPledger, i);
//				} else { //a la carte
//					if(p.isMain())
//						amount+=getMainConferencePrice(isPledger, i);
//					if(isBreakout(p.getBreakout1()) || isBreakout(p.getBreakout2()))
//						amount+=getBreakoutConferencePrice(isPledger, i);
//				}
//				if(!r.isSnefMember()) { //snef member get free post-conf
//					if(p.isPost1())
//						amount+=getPostConference1Price(isPledger, i);
//					if(p.isPost2())
//						amount+=getPostConference2Price(isPledger, i);
//				}
//			}
			p.setAmount(round(amount));
			total+=amount;
//			i++;
		}
		r.setAmount(round(total));
	}
	
	protected boolean isFullConference(ParticipantObj p) {
		return p!=null && p.isMain() 
				&& isBreakout(p.getBreakout1()) && isBreakout(p.getBreakout2());
	}
	
	protected boolean isBreakout(Integer i) {
		return i!=null && i>0;
	}
	
	protected boolean firstNFree(long n) {
		return n<2;
	}
	
	protected double getFullConferencePrice(boolean isPledger, long regNo) {
		return 100;
	}
	
	protected double getMainConferencePrice(boolean isPledger, long regNo) {
		return 80;
	}
	
	protected double getBreakoutConferencePrice(boolean isPledger, long regNo) {
		return 50;
	}
	
	protected double getPostConference1Price(boolean isPledger, long regNo) {
		return 50;
	}
	
	protected double getPostConference2Price(boolean isPledger, long regNo) {
		return 30;
	}
	
	protected double round(double d) {
		return (double) Math.round(d);
	}
	
	public void confirmAllStatus(RegisterObj r) {
		log.debug("confirmAllStatus() entry");
		setAllStatus(r, 1);
	}
	
	public void setAllStatus(RegisterObj r, int status) {
		log.debug("setAllStatus() entry");
		r.setStatus(status);
		if(r.getParticipantList()!=null)
			for(ParticipantObj p: r.getParticipantList())
				p.setStatus(status);
	}
	
	public void setAllStatusAndUpdate(List<RegisterObj> regList, RegisterFilter filter, int status) {
		for(RegisterObj r: regList)
			if(r.getId().equals(filter.getId())) {
				setAllStatus(r, status);
				update(r);
				return;
			}
	}
	
	public void postRegistrationHandling(RegisterObj r) {
		log.debug("postRegistrationHandling() entry");
		if(r==null)
			return;
		if(r.getAmount()==0)
			emailDao.sendParticipantEmail(r);
		else
			emailDao.sendChequeEmail(r);
	}
}
